LK
Автор: Максим Сохацький
Вступ до Маленького Ядра
Little Kernel (LK) — це вбудоване ядро з відкритим вихідним кодом, яке використовується в різних операційних системах і продуктах, переважно в тих, що пов’язані з низькорівневими вбудованими рішеннями, авантажувачами та безпечними середовищами. Ось список операційних систем і продуктів, які використовують LK, заснований на доступній інформації та логічних висновках із контексту проєкту LK:
Тревіс Гейзельбрехт є ключовою фігурою у створенні Little Kernel (LK) — легкого вбудованого ядра з відкритим вихідним кодом, яке стало основою для багатьох проєктів, зокрема Zircon у Fuchsia OS від Google. Він розробив LK з нуля, спираючись на свій багатий досвід роботи над операційними системами, такими як BeOS, NewOS і низькорівневими компонентами для Danger Hiptop, Apple (iPhone) та інших платформ. Гейзельбрехт створив LK як модульне, портативне ядро для вбудованих систем, зосередившись на ефективності (розмір ядра лише 15-20 КБ на ARM), підтримці багатопоточності, управлінні пам’яттю та гнучкості для різних архітектур, таких як ARM, RISC-V і x86. Його підхід до дизайну ядра відображає прагнення до простоти та продуктивності, що зробило LK популярним вибором для завантажувачів і TEE у пристроях на базі Android та інших системах.
ОС
Перелік операційних систем, що використовують або базуються на LK:
Zircon (Fuchsia OS)
Zircon — це мікроядро, яке лежить в основі операційної системи Fuchsia від Google. Воно базується на LK і було значно розширене для потреб Fuchsia. LK слугує основою для реалізації базових функцій, таких як управління потоками, пам’яттю та апаратними абстракціями. Використання: Zircon використовується в Fuchsia, яка розроблена як альтернатива Android для пристроїв IoT, смартфонів і комп’ютерів.
Trusted Little Kernel (TLK)
TLK — це відкрита реалізація технології NVIDIA для створення надійного виконання (Trusted Execution Environment, TEE) на базі LK. Вона призначена для роботи з процесорами NVIDIA Tegra. Використання: TLK інтегрується з Trusted Firmware-A (TF-A) для забезпечення безпечного виконання на SoC NVIDIA Tegra, наприклад, у пристроях із підтримкою DRM (Digital Rights Management).
Android Bootloaders
LK широко використовується як основа для завантажувачів (bootloaders) у багатьох пристроях на базі Android. Завантажувач на базі LK запускає ядро Linux і забезпечує базові функції, такі як розблокування пристрою та вибір режимів завантаження. Використання: Приклади включають пристрої від Qualcomm (наприклад, Lumia з MSM8227), Fairphone (раніші моделі), і численні Android-пристрої з чипами від різних виробників.
Trusted Execution Environment (TEE) у Android
У сучасних Android-пристроях LK часто використовується для створення TEE поряд із ядром Linux. TEE забезпечує ізольоване середовище для таких функцій, як DRM, безпечні платежі та криптографічні операції. Використання: Реалізовано в пристроях із чипами Qualcomm, MediaTek тощо, де LK адаптовано під конкретні SoC.
Платформи
Qualcomm-based Devices
LK використовується в завантажувачах для пристроїв із процесорами Qualcomm Snapdragon. Наприклад, проєкт Android4Lumia адаптував LK для Lumia на базі MSM8227. Приклад: Nokia Lumia (деякі моделі).
NXP i.MX8
NXP має форк LK (littlekernel-imx8), який адаптовано для процесорів i.MX8. Це ядро використовується в низькорівневих задачах для вбудованих систем. Використання: Промислові системи, автомобільні рішення.
Raspberry Pi Pico
LK підтримує проєкти для Raspberry Pi Pico (на базі RP2040). Хоча це не операційна система, LK може бути зібрано для запуску простих тестових програм або як основа для RTOS на цій платформі. Використання: Освітні проєкти, DIY-розробка.
Fairphone
Ранні моделі Fairphone використовували LK як завантажувач. Джерельний код для LK доступний у FairphoneMirrors на GitHub. Використання: Завантажувач для Android у Fairphone 1, 2 тощо.
Експериментальні RTOS
LK часто застосовується як основа для створення кастомних операційних систем реального часу (RTOS) у вбудованих проєктах завдяки його легкості (15-20 КБ на ARM) та модульності. QEMU Емуляція: LK підтримує тестування в QEMU для різних архітектур (ARM, RISC-V, ARM64), що робить його популярним для розробки та навчання.
Інсталяція
LK підтримує широкий сперкт платформ:
$ make list
make[1]: Entering directory '/home/tonpa/depot/bitedits/lk'
List of all buildable projects: (look in project/ directory)
armemu-test
bananapi-f3-test
dartuinoP0-bootloader
dartuinoP0-test
helio-test
lm3s6965evb-test
lpcexpresso1549-test
lpclink2-lpcboot
lpclink2-mdebug
lpcxpresso4337-test
mt6735
mt6797
nrf51-pca10000-test
nrf51-pca10028-test
nrf52-pca10040-test
nrf52-pca10056-test
nucleo-f072rb
or1ksim
pc-x86-64-test
pc-x86-legacy-test
pc-x86-test
pico-test
qemu-m4-test
qemu-microblaze-test
qemu-mips-test
qemu-sifive-u-test
qemu-virt-arm32-minimal
qemu-virt-arm32-test
qemu-virt-arm64-test
qemu-virt-m68k-test
qemu-virt-riscv32-test
qemu-virt-riscv64-supervisor-test
qemu-virt-riscv64-test
rosco-m68k-test
rpi2-test
rpi3-test
sifive-e-test
sifive-unleashed-test
stellaris-launchpad-test
stm32-h103-test
stm32-p107-test
stm32-p407-test
stm3220g-eval
stm32746g-eval2-test
stm32f4-discovery-test
stm32f429i-disco-test
stm32f746g-disco-test
uzed-bootloader
uzed-dram-test
uzed-test
vim2-test
visionfive2-test
zybo-dram-test
zybo-microblaze-test
zybo-test
make[1]: Leaving directory '/home/tonpa/depot/bitedits/lk'
Для прикладу візьмемо найпопулярнішу мобільну платформу ARM64.
$ sudo apt install lld llvm gcc-aarch64-linux-gnu \
gcc-arm-none-eabi git build-essential gcc g++ \
make qemu-system-arm
$ gmake qemu-virt-arm64-test \
'CC=/usr/bin/clang --target=aarch64-unknown-elf' \
'CPP=/usr/bin/clang-cpp --target=aarch64-unknown-elf' \
'CXX=/usr/bin/clang++ --target=aarch64-unknown-elf' \
'LD=/usr/bin/ld.lld' \
TOOLCHAIN_PREFIX=/usr/bin/llvm- \
CPPFILT=/usr/bin/llvm-cxxfilt
Запуск
$ ./scripts/do-qemuarm
qemu-system-arm -cpu cortex-a15 -m 512 -smp 1
-machine virt,highmem=off -kernel build-qemu-virt-arm32-test/lk.elf
-net none -nographic
Generic timer initialized with freq 62500000 Hz, irq 27
FDT: found memory bank range [0x40000000, 0x5fffffff] (length 0x20000000)
FDT: reserving physical range for FDT: [0x40000000, 0x400fffff]
welcome to lk/MP
boot args 0x0 0x0 0x0 0x0
INIT: cpu 0, calling hook 0x8011fab9 (version) at level 0x3ffff, flags 0x1
version:
arch: arm
platform: qemu-virt-arm
target: qemu-virt-arm
project: qemu-virt-arm32-test
buildid: O3S23_LOCAL
INIT: cpu 0, calling hook 0x801216ad (vm_preheap) at level 0x3ffff, flags 0x1
initializing heap
calling constructors
INIT: cpu 0, calling hook 0x801216f5 (vm) at level 0x4ffff, flags 0x1
initializing mp
initializing threads
initializing timers
initializing ports
creating bootstrap completion thread
top of bootstrap2()
INIT: cpu 0, calling hook 0x8011c6c1 (minip) at level 0x70000, flags 0x1
INIT: cpu 0, calling hook 0x8011d06d (pktbuf) at level 0x70000, flags 0x1
pktbuf: creating 256 pktbuf entries of size 1536 (total 393216)
INIT: cpu 0, calling hook 0x80120489 (virtio) at level 0x70000, flags 0x1
releasing 0 secondary cpus
initializing platform
FDT: found 1 cpu
PCIE: initializing pcie with ecam at 0x3f000000 found in FDT
PCI: pci ecam functions installed
PCI: last pci bus is 15
PCI dump:
bus 0
dev 0000:00:00.0 vid:pid 1b36:0008 base:sub:intr 6:0:0
PCI dump post assign:
bus 0
dev 0000:00:00.0 vid:pid 1b36:0008 base:sub:intr 6:0:0
initializing target
INIT: cpu 0, calling hook 0x801203d9 (e1000) at level 0x90001, flags 0x1
initializing apps
starting app inetsrv
starting internet servers
starting app shell
entering main console loop
]
] help
command list by block:
[aes_test]
aes_test : test AES encryption
aes_bench : bench AES encryption
[app]
app : commands to operate on apps
start : shortcut for app start
[bio]
bio : block io debug commands
[cache_tests]
cache_tests : test/bench the cpu cache
[console]
help : this list
test : test the command processor
history : command history
repeat : repeats command multiple times
[crc]
crc16 : crc16
crc32 : crc32
adler32 : adler32
bench_cksum : benchmark the checksum routines
[dcc]
dcc : dcc stuff
[float_tests]
float_tests : floating point test
[fs]
fs : fs debug commands
[fs_shell]
ls : dir listing
cd : change dir
pwd : print working dir
mkdir : make dir
mkfile : make file
rm : remove file
stat : stat file
cat : cat file
df : list mounts
[gfx]
gfx : gfx commands
[heap]
heap : heap debug commands
[kernel]
threads : list kernel threads
threadstats : thread level statistics
threadload : toggle thread load display
[mem]
dw : display memory in words
dh : display memory in halfwords
db : display memory in bytes
mw : modify word of memory
mh : modify halfword of memory
mb : modify byte of memory
fw : fill range of memory by word
fh : fill range of memory by halfword
fb : fill range of memory by byte
mc : copy a range of memory
crash : intentionally crash
panic : intentionally panic
stackstomp : intentionally overrun the stack
mtest : simple memory test
chain : chain load another binary
sleep : sleep number of seconds
sleepm : sleep number of milliseconds
time : print current time
timeh : print current time hires
[mem_tests]
mem_test : test memory
[minip]
arp : arp commands
mi : minip commands
[page_alloc]
page_alloc : page allocator debug commands
[pcitests]
pci : pci toolbox
[platform_power]
reboot : soft reset
poweroff : powerdown
[pmm]
pmm : physical memory manager
[psci]
psci_version : show psci version
[spifs]
spifs : commands related to the spifs implementation.
[stringtests]
string : memcpy tests
[tcp]
tcp : tcp commands
[tests]
printf_tests : test printf
printf_tests_float: test printf with floating point
thread_tests : test the scheduler
port_tests : test the ports
clock_tests : test clocks
bench : miscellaneous benchmarks
fibo : threaded fibonacci
spinner : create a spinning thread
cbuf_tests : test lib/cbuf
v9p_tests : test dev/virtio/9p
v9fs_tests : test lib/fs/9p
[unit_tests]
ut : run some or all of the unit tests
[version]
version : print version
[vm]
vm : vm commands
[vmm]
vmm : virtual memory manager
]
] bench
took 43866 cycles overhead to loop 1024 times
took 194670812 cycles to memset a buffer of size 1048576 1024 times (1073741824 bytes), 5.515680 bytes/cycle
took 166825809 cycles to memcpy a buffer of size 524288 1024 times (536870912 source bytes), 3.218153 source bytes/cycle
took 1528583921 cycles to manually clear a buffer using wordsize 1 of size 1048576 1024 times (1073741824 bytes), 0.702442 bytes/cycle
took 770214353 cycles to manually clear a buffer using wordsize 2 of size 1048576 1024 times (1073741824 bytes), 1.394082 bytes/cycle
took 393369633 cycles to manually clear a buffer using wordsize 4 of size 1048576 1024 times (1073741824 bytes), 2.729600 bytes/cycle
took 202183535 cycles to manually clear a buffer using wordsize 8 of size 1048576 1024 times (1073741824 bytes), 5.310728 bytes/cycle
took 176413961 cycles to manually clear a buffer of size 1048576 1024 times 8 words at a time (1073741824 bytes), 6.086490 bytes/cycle
took 183002285 cycles to manually clear a buffer of size 1048576 1024 times 8 words at a time using stm (1073741824 bytes), 5.867368 bytes/cycle
touching the floating point unit
took 144615 cycles for sin()
took 139215 cycles for cos()
took 103318 cycles for sinf()
took 100141 cycles for cosf()
took 16962 cycles for sqrt()
took 133675 cycles for sqrtf()
]
Висновки
Портування AtomVM на LK
Так, AtomVM можна розмістити на базі Little Kernel (LK) за умови певних зусиль з інтеграції. LK надає необхідні базові компоненти — середовище виконання, управління пам’яттю, підтримку потоків і доступ до апаратного забезпечення — для роботи AtomVM. Розпишемо детально процес: 1) Компіляція AtomVM як додатка або модуля LK; 2) Налаштування пам’яті та потоків LK для підтримки потреб AtomVM; 3) Прив’язку апаратних взаємодій AtomVM до платформного рівня LK; 4) Тестування об’єднаної системи на цільовому пристрої (наприклад, ESP32 або емульованій платформі QEMU). Ця комбінація використовує легкі можливості ядра LK та модель високорівневої конкурентності AtomVM, створюючи потужне вбудоване середовище для додатків на Erlang/Elixir. Наприклад, пристрій Інтернету речей (IoT) може використовувати LK для ініціалізації апаратного забезпечення, а потім запускати AtomVM для керування мережевими датчиками за допомогою моделі акторів Erlang.
Практичні міркування
Зусилля з розробки: Портизація AtomVM на LK є нетривіальним, але досяжним завданням, яке, ймовірно, потребуватиме кількох тижнів роботи досвідченого розробника вбудованих систем, знайомого з обома платформами.
Сценарій використання: Така конфігурація підходить для додатків, які потребують конкурентності Erlang на мінімальному апаратному забезпеченні, наприклад, для IoT-вузлів або спеціалізованих завантажувачів, де простота LK і абстракція AtomVM є перевагами.
Альтернативи: Для пристроїв, які вже підтримуються AtomVM (наприклад, ESP32 з ESP-IDF), використання AtomVM самостійно може бути простішим. LK додає цінність у спеціалізованих або безMMU-сценаріях.
Підсумовуючи, розміщення AtomVM на LK є технічно можливим і відповідає цілям обох систем щодо легкого вбудованого виконання. Це потребуватиме інтеграції, але може відкрити унікальні можливості для розробки вбудованих систем на Erlang.
Можливість застосування як альтернативи Ada для сертифікованого середовища
Ada є мовою програмування, широко використовуваною в сертифікованих системах (наприклад, у авіації, обороні та медицині) завдяки її сильній типізації, вбудованій обробці винятків і здатності забезпечувати надійність на рівні коду. Однак Ada часто потребує складного середовища виконання (runtime), що може бути громіздким для вбудованих систем із обмеженими ресурсами. Комбінація LK і AtomVM може слугувати легшою альтернативою, поєднуючи простоту LK із надійністю моделі Erlang, яка базується на принципах обробки помилок, описаних у дисертації Джо Армстронга "Making reliable distributed systems in the presence of software errors" (2003).
Переваги LK+AtomVM як альтернативи Ada
Надійність і обробка помилок: У своїй дисертації Армстронг наголошує на концепції "let it crash" (дозволь системі впасти), де помилки ізолюються в окремих процесах, а система відновлюється через нагляд (supervision). AtomVM реалізує цю модель, дозволяючи ізолювати збої в Erlang-процесах, що є аналогом строгій обробці винятків в Ada, але з меншим накладними витратами.
Легке ядро: LK забезпечує мінімалістичне ядро з модульною архітектурою, що дозволяє адаптувати систему до конкретних потреб сертифікації, подібно до того, як Ada використовується з RTOS (наприклад, RTEMS). На відміну від Ada, LK+AtomVM не потребує складного рантайму, що спрощує сертифікацію для DO-178C чи подібних стандартів.
Конкурентність: Модель акторів Erlang, реалізована в AtomVM, забезпечує природну конкурентність і розподіленість, що є перевагою над Ada у сценаріях, де потрібна паралельна обробка (наприклад, у системах реального часу). LK додає базові потоки, які можна оптимізувати для цього.
Мінімальний розмір: Поєднання LK (15-20 КБ ядра) і AtomVM (100-200 КБ) дозволяє розмістити систему на пристроях із 256 КБ оперативної пам’яті, що значно менше, ніж типові вимоги Ada-систем із повноцінним RTOS.
Обмеження порівняно з Ada
Сертифікація: Ada має багаторічну історію сертифікації (наприклад, SPARK для формальної верифікації), тоді як LK+AtomVM потребуватиме додаткових зусиль для доведення відповідності стандартам безпеки (наприклад, аналізу коду LK і AtomVM).
Статична типізація: Ada забезпечує статичну перевірку типів на етапі компіляції, тоді як Erlang в AtomVM використовує динамічну типізацію, що може ускладнити формальний аналіз для сертифікації.
Інструментарій: Ada має розвинені інструменти (GNAT, AdaCore), тоді як LK+AtomVM потребуватиме створення спеціалізованих засобів для верифікації та тестування.
Потенційні сценарії використання
Критичні IoT-системи: LK+AtomVM може бути використано в розумних датчиках або медичних пристроях, де потрібна надійність і легка конкурентність, а розмір коду критичний.
Авіаційні підсистеми: Як альтернатива Ada в некритичних компонентах (наприклад, моніторинг), де простота і швидкий розвиток важливіші за повну сертифікацію.
Освітні та дослідницькі проекти: Для експериментів із надійними розподіленими системами, як запропоновано Армстронгом, LK+AtomVM є доступною платформою.
Висновок щодо альтернативи Ada
LK із AtomVM може слугувати перспективною альтернативою Ada для вбудованих систем, де потрібна надійність і легка обробка помилок, натхненна ідеями Джо Армстронга. Хоча повна заміна Ada в сертифікованих середовищах потребуватиме значних зусиль для верифікації та сертифікації, ця платформа ідеально підходить для менш суворих сценаріїв або як проміжний крок до сертифікованих рішень, пропонуючи баланс між простотою, ефективністю та стійкістю до помилок.
Можливість застосування як альтернативи L4
LK із AtomVM також може розглядатися як альтернатива мікроядру L4, яке відоме своєю мінімалістичною архітектурою та широким використанням у системах реального часу й безпечних вбудованих платформах. L4 забезпечує строгу ізоляцію процесів і низькі накладні витрати завдяки малому розміру ядра (близько 10-15 КБ), але потребує складної інтеграції користувацьких сервісів для повноцінної роботи. На відміну від L4, комбінація LK і AtomVM пропонує готове рішення з підтримкою конкурентності Erlang "з коробки", що спрощує розробку розподілених систем без необхідності створення додаткових компонентів, як у L4. LK забезпечує базову модульність і апаратну абстракцію, подібну до L4, але AtomVM додає високорівневу модель акторів, що робить платформу привабливою для застосувань, де потрібна швидка розробка надійних систем, наприклад, у телекомунікаціях або IoT. Хоча LK+AtomVM може поступатися L4 у швидкості міжпроцесної взаємодії (IPC) через меншу оптимізацію ядра, вона компенсує це легкістю використання та меншою складністю розгортання, що робить її перспективною для проєктів, де пріоритетом є простота і стійкість до помилок.
LK як завантажувач для NetBSD
Little Kernel (LK) може бути простим і ефективним завантажувачем для NetBSD завдяки своїй легкій і модульній архітектурі. NetBSD, відома своєю портативністю, працює на багатьох платформах, таких як ARM, RISC-V і x86, і потребує завантажувача для ініціалізації апаратного забезпечення та запуску ядра. LK ідеально підходить для цього, оскільки підтримує різноманітні архітектури і пропонує два режими управління пам’яттю: з MMU (через pmm і vmm) і без MMU (через novm). Це дозволяє адаптувати LK до будь-яких пристроїв, від простих мікроконтролерів до складніших систем. LK швидко ініціалізує апаратне забезпечення через двоетапний процес (vm_init_preheap і vm_init_postheap), забезпечуючи мінімальні накладні витрати, а його невеликий розмір (15-20 КБ) спрощує сертифікацію для критичних систем. Як завантажувач, LK може замінити традиційні рішення, такі як U-Boot, надаючи розробникам NetBSD гнучкість і простоту для створення надійного початкового етапу завантаження.